﻿/*
 * Copyright (c) 2011, The Chancellor, Masters and Scholars of the University of Oxford
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list of 
 *   conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this list 
 *   of conditions and the following disclaimer in the documentation and/or other 
 *   materials provided with the distribution.
 *
 * Neither the name of University of Oxford nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior 
 * written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Text.RegularExpressions;

namespace Vreci.Metadata.Dicom.Silverlight
{
    public class VreciProperties
    {
        XNamespace ns = "http://vreci/properties/1/";

        bool _isFilter;

        public bool IsFilter
        {
            get
            {
                return _isFilter;
            }
            set
            {
                _isFilter = value;
            }
        }

        List<VreciPropertyItem> _properties;

        public List<VreciPropertyItem> Properties
        {
            get
            {
                return _properties;
            }
        }

        public VreciProperties()
        {
            _properties = new List<VreciPropertyItem>();
            _isFilter = false;
        }

        public static XAttribute LoadAttribute(XElement element, XNamespace ns, String attributeName)
        {
            XAttribute attribute = element.Attribute(ns + attributeName);
            if (attribute == null)
            {
                attribute = element.Attribute(attributeName);
            }

            return attribute;
        }

        /// <summary>
        /// Reads a XML document conforming to the http://vreci/properties/1/ namespace into the object.
        /// In the case of error a log is returned, otherwise null is returned.
        /// </summary>
        /// <param name="doc">The XDocument to parse from.</param>
        /// <returns>Null, or a string containg a log if there was an error.</returns>
        public string Read(XDocument doc)
        {
            //TagsToAllow = new HashSet<string>();


            //Find the properties element
            XElement Element_Properties = doc.Element(ns + "Properties");
            if (Element_Properties == null)
            {
                return "Unable to find <Properties> tag.";
            }

            //See if this is a filter
            XAttribute Attribute_Filter = LoadAttribute(Element_Properties, ns, "filter");
            if (Attribute_Filter == null)
            {
                _isFilter = false;
            }
            else
            {
                _isFilter = Attribute_Filter.Value.Trim().ToLower() == "true";
            }

            //See if there are any items           
            foreach (var Element_Item in Element_Properties.Elements(ns + "Item"))
            {
                VreciPropertyItem item = new VreciPropertyItem();
                Properties.Add(item);

                //Load non-filter items

                //Category (Optional - defaults to String.Empty)
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "category");
                    if (Attribute != null)
                    {
                        item.Category = Attribute.Value;
                    }
                    else
                    {
                        item.Category = String.Empty;
                    }
                }

                //Title
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "title");
                    if (Attribute != null)
                    {
                        item.Title = Attribute.Value;
                    }
                    else
                    {
                        return "Error <Item> tag without 'title': " + Element_Item.ToString(SaveOptions.None);
                    }
                }

                //Description
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "description");
                    if (Attribute != null)
                    {
                        item.Description = Attribute.Value;
                    }
                    else
                    {
                        item.Description = String.Empty;
                    }
                }

                //Reference
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "reference");
                    if (Attribute != null)
                    {
                        item.Reference = Attribute.Value;
                    }
                    else
                    {
                        return "Error <Item> tag without 'reference': " + Element_Item.ToString(SaveOptions.None);
                    }
                }

                //Hidden
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "hidden");
                    if (Attribute != null)
                    {
                        item.Hidden = Attribute.Value.Trim().ToLower() == "true";
                    }
                    else
                    {
                        item.Hidden = false;
                    }
                }

                //Choice
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "choice");
                    if (Attribute != null)
                    {
                        string value = Attribute.Value;

                        List<string> choices = new List<string>();
                        foreach (var s in value.Split(';'))
                        {
                            choices.Add(s.Trim());
                        }

                        item.Choices = choices;
                    }
                    else
                    {
                        item.Choices = null;
                    }
                }

                if (item.Choices != null)
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "restricttochoices");
                    if (Attribute != null)
                    {
                        item.RestrictToChoices = Attribute.Value.Trim().ToLower() == "true";
                    }
                    else
                    {
                        item.RestrictToChoices = false;
                    }
                }

                if (IsFilter)
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "usevalueasxpath");
                    if (Attribute != null)
                    {
                        item.UseValueAsXPath = Attribute.Value.Trim().ToLower() == "true";
                    }
                    else
                    {
                        item.UseValueAsXPath = false;
                    }
                }
                else
                {
                    XAttribute Attribute = LoadAttribute(Element_Item, ns, "manuallyedited");
                    if (Attribute != null)
                    {
                        item.ManuallyEdited = Attribute.Value.Trim().ToLower() == "true";
                    }
                    else
                    {
                        item.ManuallyEdited = false;
                    }

                }

                //Load the value
                if (IsFilter && item.UseValueAsXPath)
                {
                    //Remove all whitespace from code.
                    item.Value = Regex.Replace(Element_Item.Value, @"\s", "");
                }
                else
                {
                    item.Value = Element_Item.Value;
                }


            }
            return null;
        }

        public XDocument Write()
        {
            XDocument doc = new XDocument(
         
             new XElement(ns + "Properties",
                new XAttribute("xmlns", ns.NamespaceName),
                new XAttribute("filter", IsFilter),
               from p in Properties
               select p.XValue(ns,IsFilter)
                )
                );
            return doc;
        }
    }

    public class VreciPropertyItem
    {
         const string vrecinamespace = "http://vreci/dicom#";
         const string propertynamespace = "http://vreci/property#";

        string _propertyName;

        public string PropertyName
        {
            get
            {
                return _propertyName;
            }
            set
            {
                _tag = null;
                _reference = propertynamespace + value;
                _propertyName = value;
            }
        }

        string _tag;

        public string Tag
        {
            get
            {
                return _tag;
            }
            set
            {
                _propertyName = null;
                _reference = vrecinamespace + value;
                _tag = value.ToLower();
            }
        }
        string _reference;

        public string Reference
        {
            get
            {
                return _reference;
            }

            set
            {
                if (value.StartsWith(vrecinamespace))
                {
                    if (value.Equals(vrecinamespace))
                    {
                        _tag = "";
                    }
                    else
                    {
                        _tag = value.Substring(value.LastIndexOf('#') + 1);
                    }
                    _propertyName = null;

                }
                else if (value.StartsWith(propertynamespace))
                {
                    if (value.Equals(propertynamespace))
                    {
                        _propertyName = "";
                    }
                    else
                    {
                        _propertyName = value.Substring(value.LastIndexOf('#') + 1);
                    }
                    _tag = null;

                }
                else
                {
                    _tag = null;
                    _propertyName = null;
                }

                _reference = value;
            }

        }

        string _title;

        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value;
            }
        }

        string _description;

        public string Description
        {
            get
            {
                return _description;
            }
            set
            {
                _description = value;
            }
        }


        string _category;

        public string Category
        {
            get
            {
                return _category;
            }
            set
            {
                _category = value;
            }
        }
        string _value;

        public string Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
            }
        }
        bool _hidden = false;

        public bool Hidden
        {
            get
            {
                return _hidden;
            }
            set
            {
                _hidden = value;
            }
        }

        private List<string> _choices;

        public List<string> Choices
        {
            get
            {
                return _choices;
            }
            set
            {
                _choices = value;
            }
        }

        private bool _restrictToChoices = false;

        public bool RestrictToChoices
        {
            get
            {
                return _restrictToChoices;
            }
            set
            {
                _restrictToChoices = value;
            }
        }

        private bool _useValueAsXPath = false;

        public bool UseValueAsXPath
        {
            get
            {
                return _useValueAsXPath;
            }
            set
            {
                _useValueAsXPath = value;
            }
        }

        private bool _manuallyEdited = false;

        public bool ManuallyEdited
        {
            get
            {
                return _manuallyEdited;
            }
            set
            {
                _manuallyEdited = value;
            }
        }


        public VreciPropertyItem CreateInstancePropertyItem(string value)
        {
            return new VreciPropertyItem()
            {
                _category = this._category,
                _hidden = this._hidden,
                _propertyName = this._propertyName,
                _reference = this._reference,
                _tag = this._tag,
                _title = this._title,              
                _choices = this._choices,
                _description = this._description,
                _restrictToChoices = this._restrictToChoices,
                _value = value,
                _useValueAsXPath = false,
                _manuallyEdited = false
            };
        }

        public XElement XValue(XNamespace ns, bool IsFilter)
        {
            XElement e = new XElement(ns + "Item");

            if (Reference != null)
            {
                e.Add(new XAttribute(  "reference", Reference));
            }

            if (Category != null)
            {
                e.Add(new XAttribute(  "category", Category));
            }

            if (Title != null)
            {
                e.Add(new XAttribute(  "title", Title));
            }

            if (Description != null)
            {
                e.Add(new XAttribute(  "description", Description));
            }

            if (Hidden)
            {
                e.Add(new XAttribute(  "hidden", "true"));
            }

            if (Choices != null)
            {
                string choice_string = "";
                bool first = true;
                foreach (var s in Choices)
                {
                    if (first)
                    {
                        first = false;
                    }
                    else
                    {
                        choice_string += ";";
                    }
                    choice_string += s;
                }

                e.Add(new XAttribute(  "choices", choice_string));
                e.Add(new XAttribute(  "restricttochoices", RestrictToChoices));
            }

            if (IsFilter)
            {
                e.Add(new XAttribute("usevalueasxpath", UseValueAsXPath));
            }
            else
            {
                //Only applies to non-filters
                if (ManuallyEdited)
                {
                    e.Add(new XAttribute("manuallyedited", ManuallyEdited));
                }
            }

            if (Value != null)
            {
                e.Value = Value;
            }

            return e;
        }

    }
}
